package ru.amse.ilyin.builder.impl;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ru.amse.ilyin.analyzer.Analyzer;
import ru.amse.ilyin.analyzer.AnalyzerException;
import ru.amse.ilyin.analyzer.Class;
import ru.amse.ilyin.analyzer.Entity;
import ru.amse.ilyin.analyzer.Interface;
import ru.amse.ilyin.analyzer.Package;
import ru.amse.ilyin.analyzer.impl.PackageImpl;
import ru.amse.ilyin.builder.DiagramBuilder;
import ru.amse.ilyin.diagram.Diagram;
import ru.amse.ilyin.diagram.impl.ClassElement;
import ru.amse.ilyin.diagram.impl.DiagramImpl;
import ru.amse.ilyin.diagram.impl.LinkImpl;
import ru.amse.ilyin.diagram.impl.PackageElement;

/**
 * Implementation of {@link DiagramBuilder} interface.
 *
 * @author Alexander Ilyin
 * 
 * @see Analyzer
 */
public class DiagramBuilderImpl implements DiagramBuilder {
	private final Analyzer analyzer;
	
	/**
	 * Creates an instance of <code>LoaderImpl<code> class
	 * by the specified bytecode analyzer.
	 * 
	 * @param analyzer bytecode analyzer
	 */
	public DiagramBuilderImpl(Analyzer analyzer) {
		this.analyzer = analyzer;
	}
	
	public Diagram getPackageDiagram(File rootPackage) {
		final DiagramImpl result = new DiagramImpl();

		final Set<Package> packages = analyzer.getPackages(
				new File(getRootPackageName()));
		
		/*for (Package aPackage : packages) {
			try {
				for (Class aClass : aPackage.getClasses()) {
//                    System.out.println(aClass.getName() + ": " + 
//                                       aClass.getSuperclass().getName());
                    System.out.println(aClass.getName() + ":");
                    for(Interface superinterface : aClass.getSuperinterfaces()) {
                        System.out.println("  " + superinterface.getName());
                    }
                }
                for (Interface anInterface : aPackage.getInterfaces()) {
                    System.out.println(anInterface.getName() + ":");
                    for(Interface superinterface : anInterface.getSuperinterfaces()) {
                        System.out.println("  " + superinterface.getName());
                    }
                }
			} catch(AnalyzerException e) {
				System.out.println("ParserException caught");
				System.out.println(e.getMessage());
			}
		}*/
		
		try {
			final Map<Package, PackageElement> packagesToElements = 
					new HashMap<Package, PackageElement>();
			for (Package aPackage : packages) {
				final PackageElement element = new PackageElement(aPackage);
				result.addElement(element);
				packagesToElements.put(
						aPackage,
						element
				);
			}
			for (Package aPackage : packages) {
				for (Package imported : aPackage.getImportedPackages()) {
					result.addLink(new LinkImpl(
							packagesToElements.get(aPackage),
							packagesToElements.get(imported)
					));
				}
			}
		} catch(AnalyzerException e) {
            // TODO checked exception must be thrown
            throw new RuntimeException(e);
		}
		
		return result;
	}

    //TODO remove it 
    private String getRootPackageName() {
        return (new File("")).getAbsoluteFile().getParentFile().getAbsolutePath() +
				File.separator + "Business" +
				File.separator + "bin" + 
				File.separator + "ru";
    }
	
	public Diagram getClassDiagram(Package aPackage) {
		final DiagramImpl result = new DiagramImpl();
        
        try {
            final Set<Class> classes = getPackage().getClasses();
            final Set<Interface> interfaces = getPackage().getInterfaces();
            
            final Map<Entity, ClassElement> entitiesToElements = 
                    new HashMap<Entity, ClassElement>();
            
            // adding vertices
            for (Class aClass : classes) {
            	//TODO remove duplication
            	final ClassElement element = new ClassElement(aClass);
            	result.addElement(element);
                entitiesToElements.put(
                		aClass, 
                		element
                );
            }
            for (Interface anInterface : interfaces) {
            	//TODO remove duplication
            	final ClassElement element = new ClassElement(anInterface);
            	result.addElement(element);
                entitiesToElements.put(
                		anInterface, 
                		element
                );
            }
            
            // adding edges
            for (Class aClass : classes) {
                if (classes.contains(aClass.getSuperclass())) {
                	result.addLink(new LinkImpl(
                            entitiesToElements.get(aClass), 
                            entitiesToElements.get(aClass.getSuperclass())
                    ));
                }
                //TODO remove duplication
                for (Interface superinterface : aClass.getSuperinterfaces()) {
                    if (interfaces.contains(superinterface)) {
                    	result.addLink(new LinkImpl(
                                entitiesToElements.get(aClass), 
                                entitiesToElements.get(superinterface)
                        ));
                    }
                }
            }
            for (Interface anInterface : interfaces) {
                //TODO remove duplication
                for (Interface superinterface : anInterface.getSuperinterfaces()) {
                    if (interfaces.contains(superinterface)) {
                    	result.addLink(new LinkImpl(
                                entitiesToElements.get(anInterface), 
                                entitiesToElements.get(superinterface)
                        ));
                    }
                }
            }
        } catch(AnalyzerException e) {
            //TODO checked exception must be thrown 
            throw new RuntimeException(e);
        }

        return result;
	}
    
    // TODO remove it
    private Package getPackage() {
        final String name = (new File("")).getAbsoluteFile().getParentFile().getAbsolutePath() +
                        File.separator + "Business" +
                        File.separator + "bin" + 
                        File.separator + "ru" +
                        File.separator + "amse" +
                        File.separator + "ilyin" + 
                        File.separator + "diagram";
        return new PackageImpl(new File(name), "test package");
    }

	public List<String> getElements(Entity anEntity) {
		throw new UnsupportedOperationException("not implemented");
	}
}
